import type {
    UniversalFlowNode,
    UniversalFlowEdge,
    UniversalFlowNodeData,
    NodeSemanticType
} from '@/models/universalFlow';
import { v4 as uuidv4 } from 'uuid';
import { XmlDocument } from 'libxml2-wasm'; // Assuming libxml2-wasm is loaded and available
import type { ParseUniversalResult } from '../universalParser';

// Placeholder for libxml2 node types - replace with actual types from libxml2-wasm if available
type LibXml2ElementNode = any;
// Add types for Text, Comment etc. if needed and available
type LibXml2AnyNode = any;

/**
 * Parses XML content using the libxml2-wasm library and transforms
 * the result into the Universal Flow format.
 *
 * @param content The XML string content.
 * @returns A ParseUniversalResult object containing UniversalFlowNodes and Edges.
 */
export function parse(content: string): ParseUniversalResult {
    let doc: XmlDocument | null = null;
    try {
        // 1. Parse the XML string
        doc = XmlDocument.fromString(content);

        // Access root element
        const rootElement = doc.root;
        if (!rootElement) {
            throw new Error('No root element found in libxml2 document.');
        }

        // 2. Extract flow elements using libxml2 traversal
        const { nodes, edges, rootId } = transformLibXml2ToUniversal(rootElement);

        return {
            success: true,
            nodes,
            edges,
            rootId,
            rawDocument: null, // Avoid keeping libxml2 doc reference due to memory mgmt
        };

    } catch (err: any) {
        console.error('[libxml2UniversalParser] Error:', err);
        // Extract error details (libxml2 error format might vary)
        const lineMatch = err.message?.match(/line[\s:=]+(\d+)/i);
        const colMatch = err.message?.match(/col(?:umn)?[\s:=]+(\d+)/i);
        const line = err.line || (lineMatch ? parseInt(lineMatch[1], 10) : 'N/A');
        const column = err.column || (colMatch ? parseInt(colMatch[1], 10) : 'N/A');
        const message = err.message?.split('\n')[0] || 'An unexpected error occurred.';

        return {
            success: false,
            error: `libxml2 Error: ${message} (Line: ${line}, Col: ${column})`,
            nodes: [],
            edges: [],
            rootId: null,
        };
    } finally {
        // 3. IMPORTANT: Dispose the document to free up WASM memory
        if (doc) {
            try {
                doc.dispose();
                // console.log('[libxml2UniversalParser] Document disposed.');
            } catch (disposeErr) {
                console.error('[libxml2UniversalParser] Error disposing document:', disposeErr);
            }
        }
    }
}

// --- Internal Transformation Logic --- 

interface TransformLibXml2Result {
    nodes: UniversalFlowNode[];
    edges: UniversalFlowEdge[];
    rootId: string | null;
}

/**
 * Transforms the libxml2 node tree structure into the Universal Flow format.
 *
 * @param rootLibXmlNode The root node object from libxml2.
 * @returns An object containing the extracted nodes, edges, and the root ID.
 */
function transformLibXml2ToUniversal(rootLibXmlNode: LibXml2ElementNode): TransformLibXml2Result {
    const nodes: UniversalFlowNode[] = [];
    const edges: UniversalFlowEdge[] = [];
    let rootId: string | null = null;

    /**
     * Recursively processes a libxml2 node.
     *
     * @param lxNode The current libxml2 node object.
     * @param parentUniversalId The ID of the parent UniversalFlowNode (or null if root).
     * @param level The current depth level.
     * @param order The order among siblings being processed.
     * @param parentPath The path of the parent node.
     * @returns The unique ID generated for the created UniversalFlowNode, or null if skipped.
     */
    function traverse(lxNode: LibXml2AnyNode, parentUniversalId: string | null, level: number, order: number, parentPath: string): string | null {
        const nodeKind = lxNode.constructor?.name;
        let currentUniversalId: string | null = null;
        let currentNodePath: string = '';

        switch (nodeKind) {
            case 'XmlElement':
                const elementNode = lxNode as LibXml2ElementNode;
                currentUniversalId = uuidv4();
                const isRoot = !parentUniversalId;
                const nodeName = elementNode.name ? String(elementNode.name) : 'unknown';

                // Path Calculation
                const elementSegment = '/' + nodeName;
                currentNodePath = parentPath ? parentPath + elementSegment : elementSegment;

                // Attributes
                const attributes: Record<string, string> = {};
                if (elementNode.attrs && Array.isArray(elementNode.attrs)) {
                    elementNode.attrs.forEach((attr: any) => {
                        const attrName = attr.name ? String(attr.name) : null;
                        const attrValue = attr.value !== undefined ? String(attr.value) : undefined;
                        if (attrName !== null && attrValue !== undefined) {
                            attributes[attrName] = attrValue;
                        }
                    });
                }

                // Create Node Data
                const nodeData: UniversalFlowNodeData = {
                    sourceElement: null, // Avoid storing libxml2 nodes directly
                    name: nodeName,
                    nodeSemanticType: isRoot ? 'root' : 'element',
                    attributes: attributes,
                    format: 'xml',
                    path: currentNodePath,
                    level: level,
                    order: order,
                    childrenIds: [],
                    metadata: { xml: { namespace: elementNode.namespace?.href } }, // Get namespace if available
                };

                // Create Universal Node
                const universalNode: UniversalFlowNode = {
                    id: currentUniversalId,
                    type: 'universal',
                    data: nodeData,
                    position: { x: 0, y: 0 },
                };
                nodes.push(universalNode);

                // Create Edge to Parent
                if (parentUniversalId) {
                    edges.push({
                        id: uuidv4(),
                        source: parentUniversalId,
                        target: currentUniversalId,
                        type: 'smoothstep',
                        data: { relationshipType: 'parent-child' },
                    });
                } else {
                    rootId = currentUniversalId;
                }

                // Process Children
                let childOrder = 0;
                let child = elementNode.firstChild;
                while (child) {
                    const childId = traverse(child, currentUniversalId, level + 1, childOrder, currentNodePath);
                    if (childId && nodeData.childrenIds) {
                        nodeData.childrenIds.push(childId);
                        childOrder++;
                    }
                    child = child.next;
                }
                break;

            case 'XmlText':
                const textContent = (typeof lxNode.content === 'string' ? lxNode.content : '').trim();
                if (textContent && parentUniversalId) { // Only create if non-empty and has parent
                    currentUniversalId = uuidv4();
                    const textSegment = `/#text[${order}]`;
                    currentNodePath = parentPath + textSegment;

                    const textNodeData: UniversalFlowNodeData = {
                        sourceElement: null,
                        name: '',
                        value: textContent,
                        attributes: {},
                        nodeSemanticType: 'value',
                        format: 'xml',
                        path: currentNodePath,
                        level: level,
                        order: order,
                    };
                    nodes.push({
                        id: currentUniversalId,
                        type: 'universal',
                        data: textNodeData,
                        position: { x: 0, y: 0 },
                    });
                    edges.push({
                        id: uuidv4(),
                        source: parentUniversalId,
                        target: currentUniversalId,
                        type: 'smoothstep',
                        data: { relationshipType: 'parent-child' },
                    });
                }
                break;

            case 'XmlComment':
                const commentContent = (typeof lxNode.content === 'string' ? lxNode.content : '').trim();
                if (commentContent && parentUniversalId) { // Only create if non-empty and has parent
                    currentUniversalId = uuidv4();
                    const commentSegment = `/#comment[${order}]`;
                    currentNodePath = parentPath + commentSegment;

                    const commentNodeData: UniversalFlowNodeData = {
                        sourceElement: null,
                        name: '',
                        attributes: {},
                        nodeSemanticType: 'comment',
                        format: 'xml',
                        path: currentNodePath,
                        level: level,
                        order: order,
                        metadata: { comment: commentContent },
                    };
                    nodes.push({
                        id: currentUniversalId,
                        type: 'universal',
                        data: commentNodeData,
                        position: { x: 0, y: 0 },
                    });
                    edges.push({
                        id: uuidv4(),
                        source: parentUniversalId,
                        target: currentUniversalId,
                        type: 'smoothstep',
                        data: { relationshipType: 'parent-child' },
                    });
                }
                break;

            // TODO: Handle other libxml2 node types if necessary (e.g., ProcessingInstruction)

            default:
                // Skip other node types
                break;
        }

        return currentUniversalId;
    }

    // Start traversal from the root libxml2 node
    traverse(rootLibXmlNode, null, 0, 0, '');

    return { nodes, edges, rootId };
} 